From eccdcae0c8c0a985e588e7018272ab103e344249 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Tue, 9 Sep 2014 07:23:09 -0700 Subject: [PATCH] More and more tests (cargo upload) --- src/cargo/ops/cargo_upload.rs | 20 ++++++-- src/cargo/sources/git/mod.rs | 2 +- src/cargo/sources/git/utils.rs | 10 ++-- src/cargo/sources/registry.rs | 20 ++++---- tests/support/git.rs | 50 +++++++++++++++++++ tests/support/mod.rs | 2 + tests/test_cargo_package.rs | 10 ++-- tests/test_cargo_registry.rs | 72 ++++++++++++--------------- tests/test_cargo_upload.rs | 91 ++++++++++++++++++++++++++++++++++ tests/tests.rs | 3 ++ 10 files changed, 213 insertions(+), 67 deletions(-) create mode 100644 tests/support/git.rs create mode 100644 tests/test_cargo_upload.rs diff --git a/src/cargo/ops/cargo_upload.rs b/src/cargo/ops/cargo_upload.rs index 36f6b6a59..c6df621bc 100644 --- a/src/cargo/ops/cargo_upload.rs +++ b/src/cargo/ops/cargo_upload.rs @@ -8,7 +8,7 @@ use curl::http; use git2; use core::source::Source; -use core::{Package, MultiShell, SourceId}; +use core::{Package, MultiShell, SourceId, RegistryKind}; use ops; use sources::{PathSource, RegistrySource}; use util::config; @@ -34,6 +34,16 @@ pub fn upload(manifest_path: &Path, human("no upload token found, please run `cargo login`") })); let host = host.unwrap_or(try!(RegistrySource::url()).to_string()); + let host = try!(host.as_slice().to_url().map_err(human)); + let upload = { + let sid = SourceId::new(RegistryKind, host.clone()); + let mut config = try!(Config::new(shell, None, None)); + let mut src = RegistrySource::new(&sid, &mut config); + try!(src.update().chain_error(|| { + human(format!("Failed to update registry {}", host)) + })); + (try!(src.config())).upload + }; // First, prepare a tarball let tarball = try!(ops::package(manifest_path, shell)); @@ -42,8 +52,8 @@ pub fn upload(manifest_path: &Path, // Upload said tarball to the specified destination try!(shell.status("Uploading", pkg.get_package_id().to_string())); try!(transmit(&pkg, tarball, token.as_slice(), - host.as_slice()).chain_error(|| { - human(format!("failed to upload package to registry: {}", host)) + upload.as_slice()).chain_error(|| { + human(format!("failed to upload package to registry: {}", upload)) })); Ok(()) @@ -55,9 +65,8 @@ fn transmit(pkg: &Package, mut tarball: File, let url = try!(host.to_url().map_err(human)); let registry_src = SourceId::for_registry(&url); - let url = format!("{}/packages/new", host.trim_right_chars('/')); let mut handle = try!(http_handle()); - let mut req = handle.post(url.as_slice(), &mut tarball) + let mut req = handle.put(host, &mut tarball) .content_length(stat.size as uint) .content_type("application/x-tar") .header("Content-Encoding", "x-gzip") @@ -83,6 +92,7 @@ fn transmit(pkg: &Package, mut tarball: File, let response = try!(req.exec()); + if response.get_code() == 0 { return Ok(()) } // file upload url if response.get_code() != 200 { return Err(internal(format!("failed to get a 200 response: {}", response))) diff --git a/src/cargo/sources/git/mod.rs b/src/cargo/sources/git/mod.rs index 8b1ba4977..0ef4db4d6 100644 --- a/src/cargo/sources/git/mod.rs +++ b/src/cargo/sources/git/mod.rs @@ -1,4 +1,4 @@ -pub use self::utils::{GitRemote, GitDatabase, GitCheckout, GitRevision}; +pub use self::utils::{GitRemote, GitDatabase, GitCheckout, GitRevision, fetch}; pub use self::source::{GitSource, canonicalize_url}; mod utils; mod source; diff --git a/src/cargo/sources/git/utils.rs b/src/cargo/sources/git/utils.rs index 03a2ab0e0..693b96e9d 100644 --- a/src/cargo/sources/git/utils.rs +++ b/src/cargo/sources/git/utils.rs @@ -174,7 +174,8 @@ impl GitRemote { fn fetch_into(&self, dst: &git2::Repository) -> CargoResult<()> { // Create a local anonymous remote in the repository to fetch the url let url = self.url.to_string(); - fetch(dst, url.as_slice()) + let refspec = "refs/heads/*:refs/heads/*"; + fetch(dst, url.as_slice(), refspec) } fn clone_into(&self, dst: &Path) -> CargoResult { @@ -336,7 +337,8 @@ impl<'a> GitCheckout<'a> { }; // Fetch data from origin and reset to the head commit - try!(fetch(&repo, url).chain_error(|| { + let refspec = "refs/heads/*:refs/heads/*"; + try!(fetch(&repo, url, refspec).chain_error(|| { internal(format!("failed to fetch submodule `{}` from {}", child.name().unwrap_or(""), url)) })); @@ -399,9 +401,9 @@ fn with_authentication(url: &str, } } -fn fetch(repo: &git2::Repository, url: &str) -> CargoResult<()> { +pub fn fetch(repo: &git2::Repository, url: &str, + refspec: &str) -> CargoResult<()> { // Create a local anonymous remote in the repository to fetch the url - let refspec = "refs/heads/*:refs/heads/*"; with_authentication(url, &try!(repo.config()), |f| { let mut cb = git2::RemoteCallbacks::new() diff --git a/src/cargo/sources/registry.rs b/src/cargo/sources/registry.rs index 1a46c97be..82abb06ba 100644 --- a/src/cargo/sources/registry.rs +++ b/src/cargo/sources/registry.rs @@ -13,7 +13,7 @@ use url::Url; use core::{Source, SourceId, PackageId, Package, Summary, Registry}; use core::Dependency; -use sources::PathSource; +use sources::{PathSource, git}; use util::{CargoResult, Config, internal, ChainError, ToUrl, human}; use util::{hex, Require, Sha256}; use ops; @@ -32,8 +32,9 @@ pub struct RegistrySource<'a, 'b:'a> { } #[deriving(Decodable)] -struct RegistryConfig { - dl_url: String, +pub struct RegistryConfig { + pub dl: String, + pub upload: String, } #[deriving(Decodable)] @@ -75,7 +76,7 @@ impl<'a, 'b> RegistrySource<'a, 'b> { /// Decode the configuration stored within the registry. /// /// This requires that the index has been at least checked out. - fn config(&self) -> CargoResult { + pub fn config(&self) -> CargoResult { let mut f = try!(File::open(&self.checkout_path.join("config.json"))); let contents = try!(f.read_to_string()); let config = try!(json::decode(contents.as_slice())); @@ -122,7 +123,7 @@ impl<'a, 'b> RegistrySource<'a, 'b> { self.handle.as_mut().unwrap() } }; - // TODO: don't download into memory + // TODO: don't download into memory (curl-rust doesn't expose it) let resp = try!(handle.get(url.to_string()).exec()); if resp.get_code() != 200 && resp.get_code() != 0 { return Err(internal(format!("Failed to get 200 reponse from {}\n{}", @@ -162,7 +163,7 @@ impl<'a, 'b> RegistrySource<'a, 'b> { try!(fs::mkdir_recursive(&dst.dir_path(), io::UserDir)); let f = try!(File::open(&tarball)); let mut gz = try!(GzDecoder::new(f)); - // TODO: don't read into memory + // TODO: don't read into memory (Archive requires Seek) let mem = try!(gz.read_to_end()); let tar = Archive::new(MemReader::new(mem)); for file in try!(tar.files()) { @@ -229,10 +230,7 @@ impl<'a, 'b> Source for RegistrySource<'a, 'b> { // git fetch origin let url = self.source_id.get_url().to_string(); let refspec = "refs/heads/*:refs/remotes/origin/*"; - let mut remote = try!(repo.remote_create_anonymous(url.as_slice(), - refspec)); - log!(5, "[{}] fetching {}", self.source_id, url); - try!(remote.fetch(None, None).chain_error(|| { + try!(git::fetch(&repo, url.as_slice(), refspec).chain_error(|| { internal(format!("failed to fetch `{}`", url)) })); @@ -247,7 +245,7 @@ impl<'a, 'b> Source for RegistrySource<'a, 'b> { fn download(&mut self, packages: &[PackageId]) -> CargoResult<()> { let config = try!(self.config()); - let url = try!(config.dl_url.as_slice().to_url().map_err(internal)); + let url = try!(config.dl.as_slice().to_url().map_err(internal)); for package in packages.iter() { if self.source_id != *package.get_source_id() { continue } diff --git a/tests/support/git.rs b/tests/support/git.rs new file mode 100644 index 000000000..e2c9d2391 --- /dev/null +++ b/tests/support/git.rs @@ -0,0 +1,50 @@ +use std::io::{mod, fs, File}; + +use git2; + +pub struct RepoBuilder { + repo: git2::Repository, + files: Vec, +} + +pub fn repo(p: &Path) -> RepoBuilder { RepoBuilder::init(p) } + +impl RepoBuilder { + pub fn init(p: &Path) -> RepoBuilder { + fs::mkdir_recursive(&p.dir_path(), io::UserDir).unwrap(); + let repo = git2::Repository::init(p).unwrap(); + { + let mut config = repo.config().unwrap(); + config.set_str("user.name", "name").unwrap(); + config.set_str("user.email", "email").unwrap(); + } + RepoBuilder { repo: repo, files: Vec::new() } + } + + pub fn file(self, path: &str, contents: T) -> RepoBuilder { + let mut me = self.nocommit_file(path, contents); + me.files.push(Path::new(path)); + me + } + + pub fn nocommit_file(self, path: &str, + contents: T) -> RepoBuilder { + let dst = self.repo.path().dir_path().join(path); + fs::mkdir_recursive(&dst.dir_path(), io::UserDir).unwrap(); + File::create(&dst).write_str(contents.as_slice()).unwrap(); + self + } + + pub fn build(&self) { + let mut index = self.repo.index().unwrap(); + for file in self.files.iter() { + index.add_path(file).unwrap(); + } + index.write().unwrap(); + let id = index.write_tree().unwrap(); + let tree = git2::Tree::lookup(&self.repo, id).unwrap(); + let sig = git2::Signature::default(&self.repo).unwrap(); + git2::Commit::new(&self.repo, Some("HEAD"), &sig, &sig, + "Initial commit", &tree, []).unwrap(); + } +} diff --git a/tests/support/mod.rs b/tests/support/mod.rs index 8d177ef26..5eafd281c 100644 --- a/tests/support/mod.rs +++ b/tests/support/mod.rs @@ -14,6 +14,7 @@ use cargo::util::ProcessError; use support::paths::PathExt; pub mod paths; +pub mod git; /* * @@ -512,3 +513,4 @@ pub static UPDATING: &'static str = " Updating"; pub static DOCTEST: &'static str = " Doc-tests"; pub static PACKAGING: &'static str = " Packaging"; pub static DOWNLOADING: &'static str = " Downloading"; +pub static UPLOADING: &'static str = " Uploading"; diff --git a/tests/test_cargo_package.rs b/tests/test_cargo_package.rs index 5761916e0..882113cb4 100644 --- a/tests/test_cargo_package.rs +++ b/tests/test_cargo_package.rs @@ -1,8 +1,8 @@ -extern crate tar; -extern crate flate2; - use std::io::{File, MemReader}; +use tar::Archive; +use flate2::reader::GzDecoder; + use support::{project, execs, cargo_dir, ResultTest}; use support::{PACKAGING}; use hamcrest::{assert_that, existing_file}; @@ -35,9 +35,9 @@ test!(simple { execs().with_status(0).with_stdout("")); let f = File::open(&p.root().join("foo-0.0.1.tar.gz")).assert(); - let mut rdr = flate2::reader::GzDecoder::new(f); + let mut rdr = GzDecoder::new(f); let contents = rdr.read_to_end().assert(); - let ar = tar::Archive::new(MemReader::new(contents)); + let ar = Archive::new(MemReader::new(contents)); for f in ar.files().assert() { let f = f.assert(); match f.filename().unwrap() { diff --git a/tests/test_cargo_registry.rs b/tests/test_cargo_registry.rs index 6c40e4158..4ecbf4768 100644 --- a/tests/test_cargo_registry.rs +++ b/tests/test_cargo_registry.rs @@ -6,6 +6,7 @@ use serialize::hex::ToHex; use support::{ResultTest, project, execs, cargo_dir}; use support::{UPDATING, DOWNLOADING, COMPILING}; use support::paths; +use support::git::repo; use cargo::util::Sha256; use hamcrest::assert_that; @@ -30,15 +31,6 @@ fn setup() { token = "api-token" "#, reg = registry()).as_slice()).assert(); - fs::mkdir(®istry_path(), io::UserDir).assert(); - - // Init a new registry - let repo = git2::Repository::init(®istry_path()).unwrap(); - let mut config = repo.config().unwrap(); - config.set_str("user.name", "name").unwrap(); - config.set_str("user.email", "email").unwrap(); - let mut index = repo.index().unwrap(); - // Prepare the "to download" artifacts let foo = include_bin!("fixtures/foo-0.0.1.tar.gz"); let bar = include_bin!("fixtures/bar-0.0.1.tar.gz"); @@ -48,39 +40,26 @@ fn setup() { dl("pkg/bad-cksum/bad-cksum-0.0.1.tar.gz", foo); let notyet = dl("pkg/notyet/notyet-0.0.1.tar.gz", notyet); - // Prepare the registry's git repo - file(&mut index, "config.json", format!(r#" - {{"dl_url":"{}"}} - "#, dl_url()).as_slice()); - file(&mut index, "fo/oX/foo", - format!(r#"{{"name":"foo","vers":"0.0.1","deps":[],"cksum":"{}"}}"#, - foo_cksum).as_slice()); - file(&mut index, "ba/rX/bar", - format!(r#"{{"name":"bar","vers":"0.0.1","deps":["foo|>=0.0.0"],"cksum":"{}"}}"#, - bar_cksum).as_slice()); - file(&mut index, "ba/d-/bad-cksum", - format!(r#"{{"name":"bad-cksum","vers":"0.0.1","deps":[],"cksum":"{}"}}"#, - bar_cksum).as_slice()); - file(&mut index, "no/ty/notyet", - format!(r#"{{"name":"notyet","vers":"0.0.1","deps":[],"cksum":"{}"}}"#, - notyet).as_slice()); - index.remove_path(&Path::new("no/ty/notyet")).unwrap(); - - // Commit! - index.write().unwrap(); - let id = index.write_tree().unwrap(); - let tree = git2::Tree::lookup(&repo, id).unwrap(); - let sig = git2::Signature::default(&repo).unwrap(); - git2::Commit::new(&repo, Some("HEAD"), &sig, &sig, - "Initial commit", &tree, []).unwrap(); - - fn file(index: &mut git2::Index, path: &str, contents: &str) { - let dst = index.path().unwrap().dir_path().dir_path().join(path); - fs::mkdir_recursive(&dst.dir_path(), io::UserDir).assert(); - File::create(&dst).write_str(contents).unwrap(); - index.add_path(&Path::new(path)).unwrap(); + // Init a new registry + repo(®istry_path()) + .file("config.json", format!(r#" + {{"dl":"{}","upload":""}} + "#, dl_url()).as_slice()) + .file("fo/oX/foo", pkg("foo", "0.0.1", [], &foo_cksum)) + .file("ba/rX/bar", pkg("bar", "0.0.1", ["foo|>=0.0.0"], &bar_cksum)) + .file("ba/d-/bad-cksum", pkg("bad-cksum", "0.0.1", [], &bar_cksum)) + .nocommit_file("no/ty/notyet", pkg("notyet", "0.0.1", [], ¬yet)) + .build(); + + fn pkg(name: &str, vers: &str, deps: &[&str], cksum: &String) -> String { + let deps: Vec = deps.iter().map(|s| { + format!("\"{}\"", s) + }).collect(); + let deps = deps.connect(","); + + format!(r#"{{"name":"{}","vers":"{}","deps":[{}],"cksum":"{}"}}"#, + name, vers, deps, cksum) } - fn dl(path: &str, contents: &[u8]) -> String { let dst = dl_path().join(path); fs::mkdir_recursive(&dst.dir_path(), io::UserDir).assert(); @@ -114,6 +93,17 @@ test!(simple { compiling = COMPILING, dir = p.url(), reg = registry()).as_slice())); + + // Don't download a second time + assert_that(p.cargo_process("build"), + execs().with_status(0).with_stdout(format!("\ +{updating} registry `{reg}` +[..] foo v0.0.1 (the package registry) +[..] foo v0.0.1 ({dir}) +", + updating = UPDATING, + dir = p.url(), + reg = registry()).as_slice())); }) test!(deps { diff --git a/tests/test_cargo_upload.rs b/tests/test_cargo_upload.rs new file mode 100644 index 000000000..a450960a5 --- /dev/null +++ b/tests/test_cargo_upload.rs @@ -0,0 +1,91 @@ +use std::io::{mod, fs, File, MemReader}; + +use flate2::reader::GzDecoder; +use tar::Archive; +use url::Url; + +use support::{ResultTest, project, execs}; +use support::{UPDATING, PACKAGING, UPLOADING}; +use support::paths; +use support::git::repo; + +use hamcrest::assert_that; + +fn registry_path() -> Path { paths::root().join("registry") } +fn registry() -> Url { Url::from_file_path(®istry_path()).unwrap() } +fn upload_path() -> Path { paths::root().join("upload") } +fn upload() -> Url { Url::from_file_path(&upload_path()).unwrap() } + +fn setup() { + let config = paths::root().join(".cargo/config"); + fs::mkdir_recursive(&config.dir_path(), io::UserDir).assert(); + File::create(&config).write_str(format!(r#" + [registry] + host = "{reg}" + token = "api-token" + "#, reg = registry()).as_slice()).assert(); + + repo(®istry_path()) + .file("config.json", format!(r#"{{ + "dl": "", + "upload": "{}" + }}"#, upload())) + .build(); +} + +test!(simple { + let p = project("foo") + .file("Cargo.toml", r#" + [project] + name = "foo" + version = "0.0.1" + authors = [] + "#) + .file("src/main.rs", "fn main() {}"); + + assert_that(p.cargo_process("upload"), + execs().with_status(0).with_stdout(format!("\ +{updating} registry `{reg}` +{packaging} foo v0.0.1 ({dir}) +{uploading} foo v0.0.1 ({dir}) +", + updating = UPDATING, + uploading = UPLOADING, + packaging = PACKAGING, + dir = p.url(), + reg = registry()).as_slice())); + + let mut rdr = GzDecoder::new(File::open(&upload_path()).unwrap()).unwrap(); + assert_eq!(rdr.header().filename(), Some(b"foo-0.0.1.tar.gz")); + let inner = MemReader::new(rdr.read_to_end().unwrap()); + let ar = Archive::new(inner); + for file in ar.files().unwrap() { + let file = file.unwrap(); + assert!(file.filename() == Some("foo-0.0.1/Cargo.toml") || + file.filename() == Some("foo-0.0.1/src/main.rs"), + "bad filename: {}", file.filename()); + } +}) + +test!(git_deps { + let p = project("foo") + .file("Cargo.toml", r#" + [project] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies.foo] + git = "git://path/to/nowhere" + "#) + .file("src/main.rs", "fn main() {}"); + + assert_that(p.cargo_process("upload").arg("-v"), + execs().with_status(101).with_stderr("\ +failed to upload package to registry: [..] + +Caused by: + All dependencies must come from the same registry. +Dependency `foo` comes from git://path/to/nowhere instead +")); +}) diff --git a/tests/tests.rs b/tests/tests.rs index 56133c420..7ece4ff5f 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -2,9 +2,11 @@ #![feature(phase)] extern crate cargo; +extern crate flate2; extern crate git2; extern crate hamcrest; extern crate serialize; +extern crate tar; extern crate term; extern crate url; @@ -43,3 +45,4 @@ mod test_cargo_profiles; mod test_cargo_package; mod test_cargo_build_auth; mod test_cargo_registry; +mod test_cargo_upload; -- 2.30.2